﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.IO;



namespace TCP_UDP_client_server_test
{
    public class TcpServer
    {
        static private int port = 5000;

        private static int dataLength;
        private static int heartLength;
        private static int dataFreq;
        private static int heartFreq;

        private static string logComment;

        private static List<TimeSpan> heartbeatArrivalTimes = new List<TimeSpan>(); //stores the relative arrival time of each heartbeat (first heartbeat is considered time 0)
        private static  List<TimeSpan> heartbeatTimeDetlas = new List<TimeSpan>(); //stores the time difference between the arrival of two consequetive heartbeats
        private static Stopwatch stopWatch = new Stopwatch();
        private static TcpListener tcpListen;

        private static bool run = true;
        private static bool listen_ = true;

        private static void GetPort()
        {
            bool portAcquired = false;
            while (!(portAcquired))
            {
                Console.WriteLine("Port to connect to [{0}]: ", port);
                string portInput = Console.ReadLine();

                if (portInput.Length > 0)
                {
                    int dummy;
                    if (int.TryParse(portInput, out dummy))
                    {
                        if ((dummy < 65535) && (dummy > 1024))
                        {
                            port = dummy;
                            portAcquired = true;
                        }
                    }
                }
                else
                {
                    portAcquired = true;
                }
            }
        }

        protected static void CtrlCPress(object sender, ConsoleCancelEventArgs args)
        {
            run = false;
            listen_ = false;
        }

        public static void listen()
        {

            Console.CancelKeyPress += new ConsoleCancelEventHandler(CtrlCPress);

            


            GetPort();
            while (listen_)
            {
                Console.Write("Very short description of event (used part of log file name):");
                logComment = Console.ReadLine();
                tcpListen = new TcpListener(IPAddress.Any, port);
                tcpListen.Start();

                byte[] dataLengthBuffer = new byte[4]; //temp byte array 
                byte[] heartbeatLengthBuffer = new byte[4];
                byte[] dataFreqBuffer = new byte[4];
                byte[] heartbeatFreqBuffer = new byte[4];


                Console.WriteLine("\nReady to accept connection");
                TcpClient client = tcpListen.AcceptTcpClient();
                NetworkStream stream = client.GetStream();
                Console.WriteLine("Got connection");
                //read the bytes giving the length of different packets and their frequency


                stream.Read(dataLengthBuffer, 0, 4);
                stream.Read(dataFreqBuffer, 0, 4);

                stream.Read(heartbeatLengthBuffer, 0, 4);
                stream.Read(heartbeatFreqBuffer, 0, 4);


                dataLength = BitConverter.ToInt16(dataLengthBuffer, 0);
                heartLength = BitConverter.ToInt16(heartbeatLengthBuffer, 0);
                dataFreq = BitConverter.ToInt16(dataFreqBuffer, 0);
                heartFreq = BitConverter.ToInt16(heartbeatFreqBuffer, 0);
                Console.WriteLine("Data: {0} bytes every {1} ms. Heartbeat: {2} bytes every {3} ms", dataLength, dataFreq, heartFreq, heartLength);

                stopWatch.Start();
                //
                // Data packets are entirely composed of 0xff bytes while heartbeat packets are entirely composed of 0x00 bytes
                //
                try
                {
                    while (run)
                    {
                        int peekByte; //used to peek at the first byte in the stream to identify whether the message is a heartbeat packet or a data packet
                        peekByte = stream.ReadByte();
                        switch (peekByte)
                        {
                            case 255: //read the data packet off the stream
                                byte[] bufferData = new byte[dataLength];
                                stream.Read(bufferData, 0, dataLength - 1);
                                break;
                            case 0: //read the heartbeat packet off the stream and record
                                byte[] bufferHeart = new byte[heartLength];
                                stream.Read(bufferHeart, 0, heartLength - 1);
                                heartbeatArrivalTimes.Add(stopWatch.Elapsed);
                                break;
                            default:
                                //error 
                                break;
                        }
                    }
                }
                catch (System.IO.IOException e)
                {
                    //it seems easiest and most realiable way to tell if the client disconnected is if an error is thrown when attempting to read/write to the stream

                }
                finally
                {
                    calculateDelta();
                    saveDeltas();
                    tcpListen.Stop();

                }
            }
            return;
        }

        static void calculateDelta()
        {
            for (int i = 1; i < heartbeatArrivalTimes.Count; i++)
            {
                heartbeatTimeDetlas.Add(heartbeatArrivalTimes[i].Subtract(heartbeatArrivalTimes[i - 1]));
            }
        }

        static void saveDeltas()
        {
            string fileName = "TCP - " + logComment + " - " + DateTime.Now.TimeOfDay.ToString().Replace(':','.') + ".log";
            string fullFilePath = String.Format("{0}\\{1}", Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), fileName);
            
            StreamWriter file = File.AppendText(fullFilePath);

            file.WriteLine(String.Format("Data packet size:{0}", dataLength));
            file.WriteLine(String.Format("Data packet send period:{0}", dataFreq));
            file.WriteLine(String.Format("Heartbeat packet size:{0}", heartLength));
            file.WriteLine(String.Format("Heartbeat packet send period:{0}",heartFreq));

            for(int i = 0; i < heartbeatTimeDetlas.Count ; i++)
            {
                string ts = heartbeatTimeDetlas[i].TotalSeconds.ToString(".############"); //TotalSeconds is a double and its ToString method gives scientific notation; the format hack is required to get non-scientific notation
                file.WriteLine(ts);
            }

            file.Close();
            Console.WriteLine("Logged heartbeat packets arrival time difference at: {0}", fullFilePath);
        }

      

      
        






    }
}
